feat: Firebase Analytics 이벤트 계측 확장 (Phase 1/2/3, 64개)#377
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 13 minutes and 2 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (19)
📝 WalkthroughWalkthroughThis PR introduces comprehensive analytics event logging across 13 activities and 2 fragments in the presentation layer, tracking user interactions including login, course discovery, drawing, running, and profile management. It also adds new event name constants, parameter definitions, and flexible Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt (1)
76-83:⚠️ Potential issue | 🟡 MinorCapture the login method when the request starts.
These events derive
methodfrom the mutablesocialLoginfield when the callback arrives. If no provider was selected yet, or another tap changessocialLoginbefore success/failure comes back, the event gets recorded as"kakao". Persist the requested method at click time and reuse that value for the result event.Also applies to: 111-115, 123-127, 136-140
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt` around lines 76 - 83, At each click handler (cvGoogleLogin, cvKakaoLogin and the other similar handlers at the other occurrences), capture the chosen provider method into an immutable/local variable (e.g., requestedMethod) at click time instead of relying on the mutable socialLogin field; set socialLogin as before, then record requestedMethod = googleLogin.method / kakaoLogin.method (or set a separate lastRequestedMethod property) and pass or reuse that stored immutable value inside the signIn result callbacks (success/failure event recording) so the recorded method reflects the request that started rather than the current value of socialLogin when the callback runs.app/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.kt (1)
106-109:⚠️ Potential issue | 🟠 MajorDon't use data-refresh success as the source of truth for a
VIEW_*event.
VIEW_STORAGE_SCRAPfires every time this list reloads successfully — initial load, pull-to-refresh, andScreenRefreshEvent.RefreshStorageScrap— and it doesn't fire at all when the user opens the tab but the fetch fails. That will skew this screen's view count and funnel denominator. Move the view event to the fragment's visibility entry point, and keepCOURSE_COUNTon a separate load/result event if you still need it.Also applies to: 119-124, 183-186
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.kt` around lines 106 - 109, The VIEW_STORAGE_SCRAP event is currently emitted inside the refresh success path (e.g., from binding.refreshLayout.setOnRefreshListener -> getMyScrapCourses()), causing it to fire on refreshes and miss failed initial loads; move the VIEW_STORAGE_SCRAP emission out of the data-load success handlers and into the fragment visibility entry point (e.g., onResume or the fragment's entry lifecycle method) so it records a screen view when the user actually sees the fragment; leave COURSE_COUNT emission tied to the load/result event produced by getMyScrapCourses() (and any handlers for ScreenRefreshEvent.RefreshStorageScrap) so counts reflect successful fetches—apply the same change to the other occurrences referenced around the getMyScrapCourses() calls (lines noted 119–124 and 183–186).app/src/main/java/com/runnect/runnect/presentation/discover/search/DiscoverSearchActivity.kt (1)
151-154:⚠️ Potential issue | 🟡 MinorLog the keyword that produced this response, not the current EditText value.
The request is started with a local
keyword, but the success handler rereadsbinding.etDiscoverSearchTitle.text. If the user edits the field while the request is in flight, you'll emit a mismatchedKEYWORD/RESULT_COUNTpair. Carry the submitted keyword with the request or in the success state.Also applies to: 178-181
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/runnect/runnect/presentation/discover/search/DiscoverSearchActivity.kt` around lines 151 - 154, The success handler currently rereads binding.etDiscoverSearchTitle instead of using the submitted local keyword, causing KEYWORD/RESULT_COUNT mismatches if the user edits the field while the request is inflight; change the flow to carry the submitted keyword with the request (e.g., pass the local keyword into viewModel.getCourseSearch or include it in the success state/emitted result) and update the success handler to log/use that submitted keyword rather than re-reading binding.etDiscoverSearchTitle; update both occurrences (the call at getCourseSearch(...) and the corresponding success handling code) so the logged KEYWORD is the exact keyword that started the request.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/com/runnect/runnect/presentation/discover/upload/DiscoverUploadActivity.kt`:
- Around line 118-122: The event is logging Param.DISTANCE_M as a String
(uploadCourse?.distance) which breaks numeric analytics; update the
Analytics.logEvent call in DiscoverUploadActivity
(EventName.ACTION_COURSE_UPLOAD_COMPLETE) to convert uploadCourse?.distance
(from DiscoverUploadCourse.distance) to a numeric type before sending — e.g.,
safely parse to Double or Int (using a safe parse that returns null on failure),
optionally round/format as needed, and pass that numeric value (or null) for
Param.DISTANCE_M so parsing errors don’t crash and numeric aggregations work
correctly.
In `@app/src/main/java/com/runnect/runnect/presentation/draw/DrawActivity.kt`:
- Around line 497-503: The Analytics.logEvent calls in DrawActivity are sending
viewModel.distanceSum (which is in kilometers) as Param.DISTANCE_M; update both
places where Analytics.logEvent uses Param.DISTANCE_M to convert kilometers to
meters (multiply distanceSum by 1000) before passing it, taking care to handle
the distanceSum value/type/nullability (e.g., use distanceSum.value or safe-call
and convert to an Int/Double as expected by Analytics). Locate the calls to
Analytics.logEvent in DrawActivity (the blocks that include Param.DISTANCE_M to
viewModel.distanceSum.value and the similar block later) and replace the
argument with the converted meter value.
In `@app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt`:
- Around line 111-116: The current logging in LoginActivity sends the
user-facing error string (it) as Param.ERROR_CODE to Analytics.logEvent; replace
this with a stable, bounded code or category by mapping the raw message to an
enum/constant (e.g., LoginErrorCode or a function
mapLoginErrorToCode(errorMessage): String). Update the block that computes
method and calls Analytics.logEvent (the code referencing socialLogin,
GoogleLogin, EventName.ACTION_LOGIN_FAIL and Param.ERROR_CODE) to call your
mapper and pass the mapped code/category instead of the raw `it`; if no mapping
is found, emit a generic fallback code like "UNKNOWN_LOGIN_ERROR". Ensure the
mapper is deterministic and does not forward user-visible text to analytics.
In `@app/src/main/java/com/runnect/runnect/presentation/run/RunActivity.kt`:
- Around line 101-105: The analytics calls pass km-valued fields directly into
meter-named params; update each callsite (e.g., the Analytics.logEvent call
using EventName.ACTION_RUN_START with Param.TARGET_DISTANCE_M and similar calls
around lines referenced) to normalize distance to meters before logging by
converting the km value (runCourseData?.distance or distanceSum) to meters
(distanceInMeters = (value ?: 0.0) * 1000) and pass that normalized value to
Param.TARGET_DISTANCE_M / Param.COURSE_DISTANCE_M; do this conversion once per
callsite and ensure the type matches the analytics API (Int/Long/Double) when
calling Analytics.logEvent.
In `@app/src/main/java/com/runnect/runnect/util/analytics/Analytics.kt`:
- Around line 42-58: The logEvent implementation accepts unsupported Bundle
types (Int, Float, Boolean) that Firebase Analytics may not recognize; update
the loop in Analytics.logEvent to normalize params to Firebase-supported types
by converting Int -> Long (use putLong), Float -> Double (use putDouble), and
Boolean -> Long (1L/0L) or String, while keeping String, Long, and Double as-is
before calling firebaseAnalytics?.logEvent; ensure keys like COURSE_ID,
RESULT_COUNT, IS_NEW_USER are therefore stored as Long/Double/String to be
reliably processed by Firebase.
---
Outside diff comments:
In
`@app/src/main/java/com/runnect/runnect/presentation/discover/search/DiscoverSearchActivity.kt`:
- Around line 151-154: The success handler currently rereads
binding.etDiscoverSearchTitle instead of using the submitted local keyword,
causing KEYWORD/RESULT_COUNT mismatches if the user edits the field while the
request is inflight; change the flow to carry the submitted keyword with the
request (e.g., pass the local keyword into viewModel.getCourseSearch or include
it in the success state/emitted result) and update the success handler to
log/use that submitted keyword rather than re-reading
binding.etDiscoverSearchTitle; update both occurrences (the call at
getCourseSearch(...) and the corresponding success handling code) so the logged
KEYWORD is the exact keyword that started the request.
In `@app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt`:
- Around line 76-83: At each click handler (cvGoogleLogin, cvKakaoLogin and the
other similar handlers at the other occurrences), capture the chosen provider
method into an immutable/local variable (e.g., requestedMethod) at click time
instead of relying on the mutable socialLogin field; set socialLogin as before,
then record requestedMethod = googleLogin.method / kakaoLogin.method (or set a
separate lastRequestedMethod property) and pass or reuse that stored immutable
value inside the signIn result callbacks (success/failure event recording) so
the recorded method reflects the request that started rather than the current
value of socialLogin when the callback runs.
In
`@app/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.kt`:
- Around line 106-109: The VIEW_STORAGE_SCRAP event is currently emitted inside
the refresh success path (e.g., from binding.refreshLayout.setOnRefreshListener
-> getMyScrapCourses()), causing it to fire on refreshes and miss failed initial
loads; move the VIEW_STORAGE_SCRAP emission out of the data-load success
handlers and into the fragment visibility entry point (e.g., onResume or the
fragment's entry lifecycle method) so it records a screen view when the user
actually sees the fragment; leave COURSE_COUNT emission tied to the load/result
event produced by getMyScrapCourses() (and any handlers for
ScreenRefreshEvent.RefreshStorageScrap) so counts reflect successful
fetches—apply the same change to the other occurrences referenced around the
getMyScrapCourses() calls (lines noted 119–124 and 183–186).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 67ffab0c-d38a-4825-89d3-33ed81203834
📒 Files selected for processing (19)
app/src/main/java/com/runnect/runnect/presentation/countdown/CountDownActivity.ktapp/src/main/java/com/runnect/runnect/presentation/detail/CourseDetailActivity.ktapp/src/main/java/com/runnect/runnect/presentation/discover/pick/DiscoverPickActivity.ktapp/src/main/java/com/runnect/runnect/presentation/discover/search/DiscoverSearchActivity.ktapp/src/main/java/com/runnect/runnect/presentation/discover/upload/DiscoverUploadActivity.ktapp/src/main/java/com/runnect/runnect/presentation/draw/DrawActivity.ktapp/src/main/java/com/runnect/runnect/presentation/endrun/EndRunActivity.ktapp/src/main/java/com/runnect/runnect/presentation/login/GiveNicknameActivity.ktapp/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.ktapp/src/main/java/com/runnect/runnect/presentation/mypage/editname/MyPageEditNameActivity.ktapp/src/main/java/com/runnect/runnect/presentation/mypage/history/detail/MyHistoryDetailActivity.ktapp/src/main/java/com/runnect/runnect/presentation/mypage/reward/MyRewardActivity.ktapp/src/main/java/com/runnect/runnect/presentation/run/RunActivity.ktapp/src/main/java/com/runnect/runnect/presentation/scheme/SchemeActivity.ktapp/src/main/java/com/runnect/runnect/presentation/storage/StorageMyDrawFragment.ktapp/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.ktapp/src/main/java/com/runnect/runnect/presentation/storage/mydrawdetail/MyDrawDetailActivity.ktapp/src/main/java/com/runnect/runnect/util/analytics/Analytics.ktapp/src/main/java/com/runnect/runnect/util/analytics/EventName.kt
app/src/main/java/com/runnect/runnect/presentation/discover/upload/DiscoverUploadActivity.kt
Show resolved
Hide resolved
app/src/main/java/com/runnect/runnect/presentation/login/LoginActivity.kt
Show resolved
Hide resolved
12ebff9 to
409305f
Compare
| // ======================================== | ||
| // App Start / Onboarding | ||
| // ======================================== | ||
|
|
There was a problem hiding this comment.
이 파일 주석이 과하게 달렸어. 필수가 아니라면 제거해줘
코스→러닝 전환율 20% 병목 분석을 위해 Firebase 이벤트를 35개에서 64개로 확장. 12개 블랙박스 화면(RunActivity, CountDown 등)에 계측 코드 삽입. - Phase 1 (P0): 러닝 전구간, 카운트다운, 코스 그리기 완성 플로우 - Phase 2 (P1): 로그인 성공/실패, 온보딩, 검색, 업로드, 보관함, 딥링크 - Phase 3 (P2): 리워드, 프로필 수정
- DISTANCE_M 파라미터 km→m 변환 (DrawActivity, RunActivity) - Analytics.logEvent에서 Int→Long, Float→Double, Boolean→Long 변환 - LoginActivity ERROR_CODE를 안정적 상수로 변경 - DiscoverUploadActivity distance를 Double로 파싱 - EventName.kt 과도한 구분선 주석 제거
- StorageMyDrawFragment: VIEW_STORAGE_MY_DRAW 추가 - StorageScrapFragment: VIEW_STORAGE_SCRAP 추가
- click_nav_course_drawing → click_course_drawing_tab_bar - click_nav_course_discovery → click_course_discovery_tab_bar - click_nav_storage → click_storage_tab_bar - click_nav_my_page → click_my_page_tab_bar
ab04d13 to
6d2aebe
Compare
작업 배경
변경 사항
Analytics.kt파라미터 지원 logEvent 추가,EventName.kt신규 상수 29개 + Param 객체핵심 추가 이벤트
action_run_start/complete/abandon— 러닝 블랙박스 해소view_countdown,click_cancel_countdown— 러닝 직전 이탈 측정view_course_complete_result— 코스 완성 후 "바로 뛰기" vs "저장" 분기 측정click_run_from_detail— 코스 발견→러닝 전환 측정action_login_success/fail— 로그인 전환율, 장애 감지영향 범위
Test Plan
🤖 Generated with Claude Code
Summary by CodeRabbit
Telemetry